package com.eco.fanliapp.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.eco.fanliapp.common.constant.NewsEnumLog;
import com.eco.fanliapp.common.constant.WechatConfig;
import com.eco.fanliapp.common.constant.WxWithdrawal;
import com.eco.fanliapp.common.response.ServerResponse;
import com.eco.fanliapp.entity.*;
import com.eco.fanliapp.service.*;
import com.eco.fanliapp.utils.AppWxWithdrawalUtils.WXPayRequest;
import com.eco.fanliapp.utils.AppWxWithdrawalUtils.WXPayUtil;
import com.eco.fanliapp.utils.DateFormatUtils;
import com.eco.fanliapp.utils.DateUtils;
import com.eco.fanliapp.utils.MessageUtils;
import com.eco.fanliapp.utils.UUIDUtils;
import com.eco.fanliapp.vo.AppRequestParams.WxPayRequestParams;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.http.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * app微信提现
 */
@Service
public class AppWxPayMethodServiceImpl implements IAppPayMethodService {

    private static final Logger log = LoggerFactory.getLogger(AppWxPayMethodServiceImpl.class);

    @Autowired
    private WXPayRequest wxPayRequest;

    @Autowired
    private ISyUserMoneyService iSyUserMoneyService;

    @Autowired
    private IAppWithdrawalService iAppWithdrawalService;

    @Autowired
    private ISyUserMoneyLogService syUserMoneyLogService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private ISyUserWxContrastService syUserWxContrastService;

    @Autowired
    private ISmsService smsService;


    /**
     * 微信提现
     * @param syUserMoney
     * @param userid
     * @param amount
     * @param appUser
     * @param wxOpendId
     * @param type
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    public ServerResponse dealWxPayMoney(SyUserMoney syUserMoney, Long userid, String amount, AppUser appUser, String wxOpendId, int type)  throws Exception{

        //======《添加日志》======
        //添加账户变更日志
        addSyserMoneyLog(syUserMoney, userid, amount);

        //更新用户微信账户余额(SyUserMoney)
        int i = iSyUserMoneyService.wxPaySuccessUpdate(userid, amount);
        log.info("更新用户金额表微信可提现结果{}用户id{}金额{}", i, userid, amount);

        //=============《提现》==========
        //1.创建微信企业支付请求参数
        WxPayRequestParams wxPayRequestParams = new WxPayRequestParams();

        //2.填充请求参数
        BigDecimal bigDecimal1 = new BigDecimal(amount);
        BigDecimal multiply = bigDecimal1.multiply(new BigDecimal("100"));//金额单位为分
        wxPayRequestParams.setAmount(multiply.toString());//金额 *100
        wxPayRequestParams.setOpenid(wxOpendId);//用户的openid（从用户表中获取）
        wxPayRequestParams.setDesc(type==1?"省鸭APP提现":"公众号提现");//企业付款备注
        String partnerTradeNo = creatPartnerTradeNo(appUser.getUserId(), amount);
        wxPayRequestParams.setPartner_trade_no(partnerTradeNo);//订单号

        //3.请求参数转成xml
        Map map = JSON.parseObject(JSON.toJSONString(wxPayRequestParams), HashMap.class);
        String xmlStr = WXPayUtil.generateSignedXml(map,WxWithdrawal.key);
        log.info("请求微信支付接口参数：{}",xmlStr);
        String uuid = UUIDUtils.getUUID();

        //4.调用支付接口
        String  wxPayResultXml = wxPayRequest.requestWithCert(WxWithdrawal.urlSuffix, uuid, xmlStr, true);
        log.info("请求微信支付接口结果{}"+wxPayResultXml);

        //5.格式化返回结果
        Map<String, String> stringStringMap =  WXPayUtil.xmlToMap(wxPayResultXml);
        String return_code = MapUtils.getString(stringStringMap, "return_code");
        String result_code = MapUtils.getString(stringStringMap, "result_code");

        //6.处理提现结果
        //微信接口是否成功
        if(!StringUtils.isEmpty(stringStringMap) && "SUCCESS".equals(return_code)){
            //业务结果
            if("SUCCESS".equals(result_code)){
                //更新日志记录
                updateAddAppWithdrawal(appUser, 2, "微信提现成功", "微信提现成功", partnerTradeNo, amount);
                //公众号发送消息
                try {
                    String amount1 = amount + "元";
                    ServerResponse<String> respone = sendCashMessage(appUser,"30001", appUser.getWechatNickname(), DateFormatUtils.getCurrentDate(),amount1, "微信");
                    log.info("公众号发消息结果{}",JSON.toJSONString(respone));
                }catch (Exception e){

                }
                //用户账户余额信息（新增表,待处理）
                return ServerResponse.createBySuccessMessage("微信提现成功");
            }else{
                String err_code = MapUtils.getString(stringStringMap, "err_code");
                String err_code_des = MapUtils.getString(stringStringMap, "err_code_des");
                //判断失败原因是否是公司账户余额不足
                if("NOTENOUGH".equals(err_code)){
                    //给帅帅发提示短信
                    //String content = "您的企业公众号付款帐号余额不足";
                    String phone = "18569966681";
                    String time = DateUtils.date2Str(new Date(),"yyyy-MM-dd HH:mm:ss");
                    String result = null;
                    try {
                        result = smsService.sendSms3(phone, time);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    log.info("账户余额不足短信发送结果{}",result);
                }
                //已经达到今日付款总额上限/已达到付款给此用户额度上限（设置一天一次）
                if("SENDNUM_LIMIT".equals(err_code)){
                    err_code_des = "24小时内仅限提现一次哦!";
                }

                //更新日志记录
                updateAddAppWithdrawal(appUser, 3, "微信提现失败", err_code_des, partnerTradeNo, amount);
                log.info("提现业务失败原因：{}",err_code_des);
                //支付失败，手动抛出异常
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); //手动回滚失误
                return ServerResponse.createByErrorMessage(err_code_des);
            }

        }else{
            //支付失败，手动抛出异常
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); //手动回滚失误

            String return_msg = MapUtils.getString(stringStringMap, "return_msg");
            log.info("提现接口失败原因：{}",return_msg);
            return ServerResponse.createByErrorMessage(return_msg);
        }

    }

    /**
     * 添加账户变更记录
     *
     * @param userid
     * @param amount
     */
    private void addSyserMoneyLog(SyUserMoney syUserMoney, Long userid, String amount) {

        //变动前可用余额
        BigDecimal beforeBalance = syUserMoney.getCashBalanceAmount();
        log.info("用户userid:{}，账户变动前余额:{}", userid, beforeBalance);
        //提现金额
        BigDecimal amountDecimal = new BigDecimal(amount);
        //变动后可用余额
        BigDecimal afterBalance = beforeBalance.subtract(amountDecimal);
        log.info("用户userid:{}，变动后可用余额:{}", userid, afterBalance);

        SyUserMoneyLog syUserMoneyLog = new SyUserMoneyLog();
        syUserMoneyLog.setUserId(userid);
        syUserMoneyLog.setBeforeBalance(beforeBalance);
        syUserMoneyLog.setAmount(amountDecimal);
        syUserMoneyLog.setAfterBalance(afterBalance);
        syUserMoneyLog.setRemark("公众号提现");
        syUserMoneyLog.setLogStatus(1);//成功
        syUserMoneyLog.setLogType(1);//公众号

        syUserMoneyLogService.save(syUserMoneyLog);
    }

    /**
     * 生成商户订单号  年与日时分秒+用户id+金额
     * @return
     */
    private static String creatPartnerTradeNo(Long userId,String amount){
        StringBuilder stringBuilder = new StringBuilder();
        Long dateStr = DateFormatUtils.dateToString(new Date(), "yyyyMMddHHmm");
        stringBuilder.append(dateStr);
        stringBuilder.append(userId);
        stringBuilder.append(amount);
        int length = 32 - stringBuilder.toString().length();
        stringBuilder.append(RandomStringUtils.randomNumeric(length));
        log.info("生成订单号：{}",stringBuilder.toString());
        return stringBuilder.toString();
    }


    /**
     * @param appUser        用户
     * @param status         提现状态
     * @param result         处理结果
     * @param message        微信提现结果返回参数
     * @param partnerTradeNo 订单号
     */
    private void updateAddAppWithdrawal(AppUser appUser, Integer status, String result, String message, String partnerTradeNo, String amount) {

        AppWithdrawal appWithdrawal = new AppWithdrawal();
        appWithdrawal.setDeviceName("设备名称." + appUser.getUserId());
        appWithdrawal.setUserId(appUser.getUserId());
        appWithdrawal.setWechatNickname(appUser.getWechatNickname());
        appWithdrawal.setWechatId(appUser.getWechatUnionid());
        appWithdrawal.setApplicantStatus(status);
        appWithdrawal.setProcessingResults(result);
        appWithdrawal.setRemark(message);

        appWithdrawal.setWithdrawalType(2);
        appWithdrawal.setPartnerTradeNo(partnerTradeNo);
        appWithdrawal.setProcessingTime(System.currentTimeMillis());
        appWithdrawal.setTransferState(2);
        appWithdrawal.setAmount(Double.valueOf(amount));
        appWithdrawal.setApplicantTime(System.currentTimeMillis());
        iAppWithdrawalService.insertAppWithdrawal(appWithdrawal);

    }

    /**
     * 发送模板消息
     * @param u
     * @param messageType
     * @param keyword1Param
     * @param keyword2Param
     * @param keyword3Param
     * @param keyword4Param
     * @return
     * @throws ParseException
     * @throws IOException
     */
    public ServerResponse<String> sendCashMessage(AppUser u, String messageType, String keyword1Param, String keyword2Param, String keyword3Param, String keyword4Param) throws ParseException, IOException {

        if (null == u) {
            return ServerResponse.createByErrorCodeMessage(301,"Token不正确");
        }

        //获取openId
        QueryWrapper<SyUserWxContrast> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("union_id", u.getWechatUnionid());
        queryWrapper.eq("enable", true);
        queryWrapper.eq("deleted", false);
        List<SyUserWxContrast> listWxContrast = syUserWxContrastService.list(queryWrapper);
        SyUserWxContrast contrast = null;
        String openId = "";
        if (org.apache.commons.collections.CollectionUtils.isNotEmpty(listWxContrast)) {
            //已经关注公众号，返回提示
            openId = listWxContrast.get(0).getWxOpendId();
        }

        //校验参数
        if (openId == null || "".equals(openId)) {
            return ServerResponse.createByErrorMessage("该用户未关注公众号:" + u.getUserId());
        }
        if (messageType == null) {
            return ServerResponse.createByErrorMessage("参数不能为空:" + messageType);
        }

        String accessToken = getAccessToken();
        //区分消息模板id
        String templateId = "";
        String firstParam = "";
        String remarkParam = "";
        Integer newsType = null;
        if("30001".equals(messageType)){
            templateId = WechatConfig.ARRIVAL_ACCOUNT_TEMPLATE_ID;//提现到账通知
            firstParam = "你好，您已成功提现";
            remarkParam = "详情请进入省鸭App中查看";
            //remarkParam = "现金已成功到账您的微信钱包，详情查看《我》-《钱包》-《零钱明细》";
            newsType = NewsEnumLog.NEWS_ARRIVAL_ACCOUNT.getNewsType();
        }
        if("30002".equals(messageType)){
            templateId = WechatConfig.CHECKOUT_SUCCESS_TEMPLATE_ID;//下单成功提醒
            firstParam = "下单成功";
            remarkParam = "此次帮您节省" + keyword4Param + "元";
            newsType = NewsEnumLog.NEWS_CHECKOUT_SUCCESS.getNewsType();
        }
        if("30003".equals(messageType)){
            templateId = WechatConfig.SUCCESSFUL_DELIVERY_TEMPLATE_ID;//收货成功提醒
            firstParam = "收货成功提醒";
            remarkParam = "冻结补贴" + keyword4Param + "元";
            newsType = NewsEnumLog.NEWS_SUCCESSFUL_DELIVERY.getNewsType();
        }
        if("30004".equals(messageType)){
            templateId = WechatConfig.FRIENDS_SUCCEED_TEMPLATE_ID;//好友下单成功提醒
            firstParam = "好友下单成功";
            remarkParam = "详情请进入省鸭App中查看";
            newsType = NewsEnumLog.NEWS_FRIENDS_SUCCEED.getNewsType();
        }

        if("30005".equals(messageType)){
            templateId = WechatConfig.INVITE_FRIENDS_TEMPLATE_ID;//邀请好友成功
            firstParam = "邀请好友成功";
            remarkParam = "详情请进入省鸭App中查看";
            newsType = NewsEnumLog.NEWS_INVITE_FRIENDS.getNewsType();
        }

        if("30006".equals(messageType)){
            templateId = WechatConfig.SUBSIDY_ARRIVAL_TEMPLATE_ID;//补贴到账通知
            firstParam = "补贴到账";
            remarkParam = "详情请进入省鸭App中查看";
            newsType = NewsEnumLog.NEWS_SUBSIDY_ARRIVAL.getNewsType();
        }

        if ("".equals(templateId)) {
            return ServerResponse.createByErrorMessage("传入的模板类型不存在！");
        }
        if (newsType == null) {
            return ServerResponse.createByErrorMessage("未匹配到消息类型！");
        }
        //组装数据发送消息
        log.info("用户openId{},消息模板id{},消息头{}，消息体1{}，消息体2{}，消息体3{}，消息体4{}，消息尾{}", openId, templateId, firstParam, keyword1Param, keyword2Param, keyword3Param, keyword4Param, remarkParam);
        Map<String, String> map = WeixinUtil.handleMessage(accessToken, openId, templateId, firstParam, keyword1Param, keyword2Param, keyword3Param, keyword4Param, remarkParam);
        String newsContent = map.get("newsContent");//消息内容
        String aBoolean = map.get("result");//消息结果
        log.info("消息内容{}，消息结果{}", newsContent, aBoolean);
        return ServerResponse.createBySuccess("消息发送结果", "true".equals(aBoolean) ? "成功" : "失败");
    }

    /**
     * 获取accessToken
     * @return
     */
    public String getAccessToken() throws IOException {
        //从redis中获取
        String accessToken =(String)redisTemplate.opsForValue().get("access_token");
        log.info("从redis中获取的accessToken" + accessToken);
        if(accessToken == null || "".equals(accessToken) ){
            //生成新的access_token
            accessToken = WeixinUtil.getAccessToken();
            redisTemplate.opsForValue().set("access_token",accessToken, 3600, TimeUnit.SECONDS);
        }
        return accessToken;
    }

}
